/* 
 * Copyright (c), nFractals Ltd. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.parade.distributed;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;

import org.parade.driver.Driver;

import junit.framework.TestCase;

public class CommonLockTestCase extends TestCase{
	
	long counter = 0;
	private Driver driverA;
	private Driver driverB;

	public CommonLockTestCase(){
	
	}
	
	public CommonLockTestCase(Driver driverA, Driver driverB) throws InterruptedException{
		this.driverA = driverA;
		this.driverB = driverB;
		basic();
		tryLock();
		nestedLock();
		Condition();
	}
	
	public void testDummy(){
		assertTrue(true);
	}
	
	private void basic() throws InterruptedException{
		
		java.lang.Thread t1 = new java.lang.Thread(){
			@Override
			public void run() {
				org.parade.distributed.Lock lock = new Lock(driverA, new Id("Lock"));
				for(int i=0; i<10000; i++){
					lock.lock();
					counter++;
					lock.unlock();
				}
			};
		};
		
		java.lang.Thread t2 = new java.lang.Thread(){
			@Override
			public void run() {
				org.parade.distributed.Lock lock = new Lock(driverB, new Id("Lock"));
				for(int i=0; i<10000; i++){
					lock.lock();
					counter++;
					lock.unlock();
				}
			};
		};
		
		
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		
		assertEquals(counter, 20000);
	}
	
	private void tryLock() throws InterruptedException{
		counter=0;
		java.lang.Thread t1 = new java.lang.Thread(){
			@Override
			public void run() {
				org.parade.distributed.Lock lock = new Lock(driverA, new Id("Lock"));
				lock.lock();
				counter++;
				while(counter==1)java.lang.Thread.yield();
				lock.unlock();

			};
		};
		t1.start();
		while(counter==0) java.lang.Thread.yield();
		org.parade.distributed.Lock lock = new Lock(driverB, new Id("Lock"));		
		assertFalse(lock.tryLock());
		
		long start = System.currentTimeMillis();
		assertFalse(lock.tryLock(500, TimeUnit.MILLISECONDS));
		assertTrue(System.currentTimeMillis() - start >= 500);
		
		counter++;
		t1.join();
	}
	
	
	private void nestedLock(){
		org.parade.distributed.Lock lock = new Lock(driverA, new Id("lock"));
		lock.lock();
		lock.lock();
		lock.unlock();
		lock.unlock();
	}
	
	private void Condition() throws InterruptedException{
		counter=0;
		java.lang.Thread t1 = new java.lang.Thread(){
			@Override
			public void run(){
				try {
					org.parade.distributed.Lock lock = new Lock(driverA, new Id("Lock"));
					Condition condA = lock.newCondition();
					counter++;
					lock.lock();
					condA.await();
					lock.unlock();
					counter++;
				} catch (InterruptedException e) {
				}				 
			};
		};
		t1.start();
		while(counter==0) java.lang.Thread.yield();
		
		org.parade.distributed.Lock lock = new Lock(driverB, new Id("Lock"));
		Condition condB = lock.newCondition();
		lock.lock();
		condB.signal();
		lock.unlock();
		while(counter<2) java.lang.Thread.yield();
		t1.join();
	}
}
